package com.googlecode.tabletags.components;

import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.util.MakeIterator;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import org.apache.struts2.views.jsp.IteratorStatus;

import com.opensymphony.xwork2.util.ValueStack;

/**
 * Renders a html table. This is a cross between an iterator and a ftl based UI
 * tag.
 */
@StrutsTag(name = "table", tldTagClass = "com.googlecode.tabletags.views.jsp.ui.TableTag", description = "Renders a html table")
public class Table extends AbstractURLComponent {
    private static final Log LOG = LogFactory.getLog(Table.class);
    public static final String TABLE_HEADER = "table-header";
    public static final String TEMPLATE = "table-footer";
    public static final String ROW_START = "table-rowstart";
    public static final String ROW_END = "table-rowend";
    protected Iterator iterator;
    protected IteratorStatus status;
    protected Object oldStatus;
    protected IteratorStatus.StatusState statusState;
    protected String statusAttr;
    protected List<Column> columns = new ArrayList<Column>();
    private int rowNum = -1;

    // Pager attributes
    protected String pagerCommand;
    protected String pagerValue;

    // SortableHeader attributes
    protected String sortOrder;
    protected String sortName;
    protected String sortValue;

    //Dojo attributes
    protected String clientSort;
    /**
     * @param stack
     * @param request
     * @param response
     */
    public Table(ValueStack stack, HttpServletRequest request,
        HttpServletResponse response) {
        super(stack, request, response);
    }

    protected void evaluateExtraParams() {
        super.evaluateExtraParams();

        if(pagerCommand != null) {
            parameters.put("pagerCommand", pagerCommand);
        }
        if(pagerValue != null) {
            parameters.put("pagerValue", pagerValue);
        }

        parameters.put("columns", columns);
        parameters.put("sortOrder", sortOrder);
        parameters.put("sortName", sortName);
        parameters.put("sortValue", sortValue);
        
        if(clientSort != null)
            addParameter("clientSort", findValue(clientSort, Boolean.class));
    }

    /**
     * @see org.apache.struts2.components.ClosingUIBean#start(java.io.Writer)
     */
    public boolean start(Writer writer) {
        if(initializeIterator()) {
            super.start(writer);

            evaluateParams();

            return true;
        } else {
            super.end(writer, "");

            return false;
        }
    }

    /**
     * @return true if the iterator has data, otherwise false.
     */
    private boolean initializeIterator() {
        // Create an iterator status if the status attribute was set.
        if(statusAttr != null) {
            statusState = new IteratorStatus.StatusState();
            status = new IteratorStatus(statusState);
        }

        ValueStack stack = getStack();

        if(value == null) {
            value = "top";
        }

        iterator = MakeIterator.convert(findValue(value));

        // get the first
        if((iterator != null) && iterator.hasNext()) {
            Object currentValue = iterator.next();

            stack.push(currentValue);

            String id = getId();

            if((id != null) && (currentValue != null)) {
                stack.getContext().put(id, currentValue);
            }

            // Status object
            if(statusAttr != null) {
                statusState.setLast(!iterator.hasNext());
                oldStatus = stack.getContext().get(statusAttr);
                stack.getContext().put(statusAttr, status);
            }
            return true;
        } else {
            return false;
        }
    }

    private boolean nextIteratorValue() {
        ValueStack stack = getStack();

        if(iterator != null) {
            stack.pop();
        }

        if((iterator != null) && iterator.hasNext()) {
            Object currentValue = iterator.next();

            stack.push(currentValue);

            String id = getId();

            if((id != null) && (currentValue != null)) {
                // pageContext.setAttribute(id, currentValue);
                // pageContext.setAttribute(id, currentValue,
                // PageContext.REQUEST_SCOPE);
                stack.getContext().put(id, currentValue);
            }

            // Update status
            if(status != null) {
                statusState.next(); // Increase counter
                statusState.setLast(!iterator.hasNext());
            }
            return true;
        } else {
            return false;
        }
    }

    private void cleanupIterator() {
        // Reset status object in case someone else uses the same name in
        // another iterator tag instance
        if(status != null) {
            if(oldStatus == null) {
                stack.getContext().put(statusAttr, null);
            } else {
                stack.getContext().put(statusAttr, oldStatus);
            }
        }
    }

    /**
     * @see org.apache.struts2.components.UIBean#end(java.io.Writer,
     *      java.lang.String)
     */
    public boolean end(Writer writer, String body) {
        rowNum++;
        parameters.put("rowIndex", rowNum);
        if(rowNum == 0) {
            try {
                mergeTemplate(writer, buildTemplateName(null, TABLE_HEADER));
                mergeTemplate(writer, buildTemplateName(null, ROW_START));
            } catch(Exception e) {
                LOG.error("Could not open template", e);
                e.printStackTrace();
            }
            return true;
        }

        try {
            mergeTemplate(writer, buildTemplateName(null, ROW_END));
        } catch(Exception e) {
            LOG.error("Could not open template", e);
            e.printStackTrace();
        }

        if(nextIteratorValue()) {
            try {
                mergeTemplate(writer, buildTemplateName(null, ROW_START));
            } catch(Exception e) {
                LOG.error("Could not open template", e);
                e.printStackTrace();
            }
            return true;
        } else {
            cleanupIterator();
            try {
                super.end(writer, body, false);
                mergeTemplate(writer, buildTemplateName(template,
                    getDefaultTemplate()));
            } catch(Exception e) {
                LOG.error("Could not open template", e);
                e.printStackTrace();
            }
            return false;
        }
    }

    /**
     * @see org.apache.struts2.components.UIBean#getDefaultTemplate()
     */
    protected String getDefaultTemplate() {
        return TEMPLATE;
    }

    @StrutsTagAttribute(description = "the status id")
    public void setStatus(String status) {
        this.statusAttr = status;
    }

    @StrutsTagAttribute(description = "the sortableHeader name")
    public void setSortName(String sortName) {
        this.sortName = sortName;
    }

    @StrutsTagAttribute(description = "the sortableHeader sortOrder")
    public void setSortOrder(String sortOrder) {
        this.sortOrder = sortOrder;
    }

    @StrutsTagAttribute(description = "the sortableHeader value")
    public void setSortValue(String sortValue) {
        this.sortValue = sortValue;
    }

    @StrutsTagAttribute(description = "the action property for the pager command")
    public void setPagerCommand(String pagerCommand) {
        this.pagerCommand = pagerCommand;
    }

    @StrutsTagAttribute(description = "the action property for the PagerDisplay")
    public void setPagerValue(String pagerValue) {
        this.pagerValue = pagerValue;
    }

    public int getRowNum() {
        return rowNum;
    }

    /**
     * @param header header to be added to this table
     */
    public void addColumn(Column header) {
        this.columns.add(header);
    }

    /**
     * @return the headers
     */
    public List<Column> getColumns() {
        return columns;
    }

    @StrutsTagAttribute(description = "Use javascript for sorting", defaultValue="false")
    public void setClientSort(String clientSort) {
        this.clientSort = clientSort;
    }

    String getClientSort() {
        return clientSort;
    }
}